package com.dragon.codefx.utils;

import cn.hutool.core.util.ZipUtil;
import cn.hutool.extra.template.*;
import com.dragon.codefx.dataBase.controller.bo.GenerateBO;
import com.dragon.codefx.model.GenerateTemplate;
import com.dragon.field.FieldUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 代码生成工具类
 *
 * @author lsc
 * @since 2024/12/1 16:07
 **/
@Slf4j
public class GenerateUtil {

    private static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir");

    @SneakyThrows
    public static File generate(GenerateBO generateBO) {
        String tablePrefix = generateBO.getGenerateInfo().getTablePrefix();
        String tableName = generateBO.getGenerateInfo().getTableName();
        String packName = FieldUtil.underlineToHump(tableName.replace(tablePrefix, ""));
        String className = FieldUtil.capitalize(packName);

        String tempPath = SYS_TEM_DIR + "generate" + File.separator + "temp" + File.separator + packName + File.separator;

        Map<String, Object> map = new HashMap<>();
        map.put("tableComment", generateBO.getGenerateInfo().getTableComment());
        map.put("author", generateBO.getGenerateInfo().getAuthor());
        map.put("since", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        map.put("tableName", tableName);
        map.put("packName", packName);
        map.put("className", className);
        map.put("columns", generateBO.getGenerateInfo().getTableColumns());
        map.put("parentsField", generateBO.getGenerateInfo().getParentsField());

        List<GenerateTemplate> adminTemplates = getAdminFile(generateBO.getGenerateInfo().getGenerateMethod(), generateBO.getGenerateInfo().getGenerateXml());
        List<GenerateTemplate> frontTemplates = getFrontFile();
        for (GenerateTemplate template : adminTemplates) {
            TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig(template.getPath(), TemplateConfig.ResourceMode.CLASSPATH));
            Template engineTemplate = engine.getTemplate(template.getTemplate());
            String filePath = tempPath + "admin/" + packName + "/" + template.getModule() + "/" + className + template.getSuffix();
            File file = new File(filePath);
            map.put("package", generateBO.getGenerateInfo().getBasePackage() + "." + packName);
            genFile(file, engineTemplate, map);
        }
        for (GenerateTemplate template : frontTemplates) {
            TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig(template.getPath(), TemplateConfig.ResourceMode.CLASSPATH));
            Template engineTemplate = engine.getTemplate(template.getTemplate());
            String filePath = tempPath + "front/" + template.getModule() + "/" + packName + "/" + className + template.getSuffix();
            File file = new File(filePath);
            genFile(file, engineTemplate, map);
        }
        File file = new File(tempPath);
        String zipPath = file.getPath() + ".zip";
        ZipUtil.zip(file.getPath(), zipPath);

        return new File(zipPath);

    }

    @SneakyThrows
    public static List<Map<String, String>> preview(GenerateBO generateBO) {
        String tablePrefix = generateBO.getGenerateInfo().getTablePrefix();
        String tableName = generateBO.getGenerateInfo().getTableName();
        String packName = FieldUtil.underlineToHump(tableName.replace(tablePrefix, ""));
        String className = FieldUtil.capitalize(packName);

        Map<String, Object> map = new HashMap<>();
        map.put("tableComment", generateBO.getGenerateInfo().getTableComment());
        map.put("author", generateBO.getGenerateInfo().getAuthor());
        map.put("since", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        map.put("tableName", tableName);
        map.put("packName", packName);
        map.put("className", className);
        map.put("columns", generateBO.getGenerateInfo().getTableColumns());
        map.put("parentsField", generateBO.getGenerateInfo().getParentsField());

        List<GenerateTemplate> adminTemplates = getAdminFile(generateBO.getGenerateInfo().getGenerateMethod(), generateBO.getGenerateInfo().getGenerateXml());
        List<GenerateTemplate> frontTemplates = getFrontFile();
        List<Map<String, String>> resultList = new ArrayList<>();
        for (GenerateTemplate template : adminTemplates) {
            TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig(template.getPath(), TemplateConfig.ResourceMode.CLASSPATH));
            Template engineTemplate = engine.getTemplate(template.getTemplate());
            map.put("package", generateBO.getGenerateInfo().getBasePackage() + "." + packName);

            Map<String, String> resultMap = new HashMap<>();
            resultMap.put("name", template.getLabel());
            resultMap.put("content", engineTemplate.render(map));
            resultList.add(resultMap);
        }

        for (GenerateTemplate template : frontTemplates) {
            TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig(template.getPath(), TemplateConfig.ResourceMode.CLASSPATH));
            Template engineTemplate = engine.getTemplate(template.getTemplate());

            Map<String, String> resultMap = new HashMap<>();
            resultMap.put("name", template.getLabel());
            resultMap.put("content", engineTemplate.render(map));
            resultList.add(resultMap);
        }
        return resultList;
    }

    private static List<GenerateTemplate> getAdminFile(String path, Boolean generateXml) {
        boolean isMybatis = path.equals("mybatis");
        List<GenerateTemplate> templates = Arrays.asList(
                new GenerateTemplate("template/admin/" + path, "Entity", "Entity.ftl", "entity", "Entity.java"),
                new GenerateTemplate("template/admin/" + path, isMybatis ? "Mapper" : "Repository", isMybatis ? "Mapper.ftl" : "Repository.ftl", isMybatis ? "mapper" : "repository", isMybatis ? "Mapper.java" : "Repository.java"),
                new GenerateTemplate("template/admin/" + path, "ServiceImpl", "ServiceImpl.ftl", "service/impl", "ServiceImpl.java"),
                new GenerateTemplate("template/admin/" + path, "Service", "Service.ftl", "service", "Service.java"),
                new GenerateTemplate("template/admin/" + path, "Controller", "Controller.ftl", "controller", "Controller.java"),
                new GenerateTemplate("template/admin/" + path, "AddDTO", "AddDTO.ftl", "controller/dto", "AddDTO.java"),
                new GenerateTemplate("template/admin/" + path, "UpdateDTO", "UpdateDTO.ftl", "controller/dto", "UpdateDTO.java"),
                new GenerateTemplate("template/admin/" + path, "SearchDTO", "SearchDTO.ftl", "controller/dto", "SearchDTO.java"),
                new GenerateTemplate("template/admin/" + path, "SearchVO", "SearchVO.ftl", "controller/vo", "VO.java"),
                new GenerateTemplate("template/admin/" + path, "ExportVO", "ExportVO.ftl", "controller/vo", "ExportVO.java")
        );

        if (generateXml && path.equals("mybatis")) {
            List<GenerateTemplate> mutableTemplates = new ArrayList<>(templates);
            mutableTemplates.add(new GenerateTemplate("template/admin/mybatis", "MapperXml", "MapperXml.ftl", "mapper/xml", "Mapper.xml"));
            return mutableTemplates;
        }

        return templates;
    }

    private static List<GenerateTemplate> getFrontFile() {
        return Arrays.asList(
                new GenerateTemplate("template/front", "Api", "PageApi.ftl", "api", "Api.ts"),
                new GenerateTemplate("template/front", "Type", "PageTypes.ftl", "types", "Type.ts"),
                new GenerateTemplate("template/front", "Param", "PageParameter.ftl", "params", "Param.ts"),
                new GenerateTemplate("template/front", "Rules", "PageRules.ftl", "rules", "Rule.ts"),
                new GenerateTemplate("template/front", "View", "PageView.ftl", "views", "View.vue")
        );
    }

    private static void genFile(File file, Template template, Map<String, Object> map) throws IOException {
        // 生成目标文件
        Writer writer = null;
        try {
            // 检查文件的父目录是否存在，如果不存在则创建
            if (file.getParentFile() != null && !file.getParentFile().exists()) {
                boolean dirsCreated = file.getParentFile().mkdirs();
//                if (dirsCreated) {
//                    System.out.println("创建文件夹");
//                } else {
//                    System.out.println("文件夹已存在不用创建");
//                }
            }
//            System.out.println("生成文件的位置: " + file.getAbsolutePath());
            writer = new FileWriter(file);
            template.render(map, writer);
        } catch (TemplateException | IOException e) {
            throw new RuntimeException("生成文件时出错", e);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

}
